﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PSE.Framework.ErrorLogging;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
using System.Diagnostics;
using PSE.Framework.Configuration;
using PSE.LogPackager.Common.Configuration;
using PSE.Framework.ErrorLogging.Configuration;
using PSE.LogPackager.Common.Service;

namespace PSE.LogPackager
{
    public class LogPackager
    {
        private const string LOG_PACKAGE_PATH = @"LogPackages\LogPackage_{0:yyyy-MM-dd_HH-mm-ss}.zip";
        private const string PERIOD_LOG_PACKAGE_PATH = @"LogPackages\LogPackage_{0:yyyy-MM-dd}_{1:yyyy-MM-dd}.zip";

        private LogPackagerConfigurationSection _logPackagerConfiguration;

        /// <summary>
        /// Construtor padrão
        /// </summary>
        public LogPackager()
        {
            _logPackagerConfiguration = ApplicationConfiguration.GetCurrent().GetSection<LogPackagerConfigurationSection>("logPackagerConfiguration");
        }

        /// <summary>
        /// E-mail de suporte
        /// </summary>
        public string SupportEmail
        {
            get { return _logPackagerConfiguration.SupportEmail; }
        }

        /// <summary>
        /// Empacota informações de log
        /// </summary>
        /// <param name="logData">Log a ser empacotado</param>
        /// <param name="files">Lista de arquivos a serem empacotados</param>
        /// <returns>Nome do arquivo gerado</returns>
        public string Pack(Uri sourceAddress, LogData logData)
        {
            return this.Pack(sourceAddress, logData, null);
        }

        /// <summary>
        /// Empacota informações de log
        /// </summary>
        /// <param name="logData">Log a ser empacotado</param>
        /// <param name="files">Lista de arquivos a serem empacotados</param>
        /// <returns>Nome do arquivo gerado</returns>
        public string Pack(Uri sourceAddress, LogData logData, Window window)
        {
            string logPackagePath = null;
            string logDataFile = null;
            string screenshotFile = null;

            string clientPSESetupPath = null;
            string clientPSEComponentsSetupPath = null;
            string clientSystemSetupPath = null;

            string licenceFile = null;
            string serverSetupPath = null;

            TraceEventType traceEventType;
            string systemSourceDescription = string.Format("{0} - {1}", logData.System.Description, logData.Source.Description);

            switch (logData.Type)
            {
                case LogType.Information:
                    traceEventType = TraceEventType.Information;
                    break;

                case LogType.Warning:
                    traceEventType = TraceEventType.Warning;
                    break;

                case LogType.Error:
                    traceEventType = TraceEventType.Error;
                    break;

                default:
                    traceEventType = TraceEventType.Error;
                    break;
            }

            // Cria arquivo com o erro
            logDataFile = Path.Combine(Path.GetTempPath(), @"Log.svclog");

            if (File.Exists(logDataFile))
                File.Delete(logDataFile);

            using (XmlWriterTraceListener xmlWriterTraceListener = new XmlWriterTraceListener(logDataFile))
            {
                xmlWriterTraceListener.TraceData(null, systemSourceDescription, traceEventType, -1, Environment.NewLine + logData.ToString());
                xmlWriterTraceListener.Flush();
            }

            if (!File.Exists(logDataFile))
                throw new Exception(string.Format("Unable to generate '{0}' log file.", logDataFile));


            // Coleta screenshot com o erro
            try
            {
                if (window != null && window.IsLoaded)
                {
                    screenshotFile = Path.Combine(Path.GetTempPath(), string.Format(@"{0}_Screenshot.jpg", window.GetType().Name));
                    this.SaveScreenshot(window, screenshotFile);
                }
            }
            catch (Exception ex)
            {
                HandleException(logData.System, new Exception(string.Format("Unable to get '{0}' screenshot.", window.GetType().Name), ex));
                screenshotFile = null;
            }


            // Captura arquivos de setup
            clientPSESetupPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), @"BMFBOVESPA\PSE");
            if (!Directory.Exists(clientPSESetupPath))
            {
                HandleException(logData.System, new Exception(string.Format("Client setup file '{0}' not found.", clientPSESetupPath)));
                clientPSESetupPath = null;
            }

            clientPSEComponentsSetupPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), @"BMFBOVESPA\PSEComponents");
            if (!Directory.Exists(clientPSEComponentsSetupPath))
            {
                HandleException(logData.System, new Exception(string.Format("Client setup file '{0}' not found.", clientPSEComponentsSetupPath)));
                clientPSEComponentsSetupPath = null;
            }

            clientSystemSetupPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), string.Format(@"BMFBOVESPA\{0}", logData.System.Name));
            if (!Directory.Exists(clientSystemSetupPath))
            {
                HandleException(logData.System, new Exception(string.Format("Client setup file '{0}' not found.", clientSystemSetupPath)));
                clientSystemSetupPath = null;
            }


            if (sourceAddress != null)
            {
                LogPackagerServiceClient logPackagerServiceClient = new LogPackagerServiceClient();

                try
                {
                    // Baixa o arquivo de licença
                    licenceFile = Path.Combine(Path.GetTempPath(), string.Format(@"{0}.lic", logData.System.Name));

                    if (!logPackagerServiceClient.DownloadLicenceFile(licenceFile))
                        licenceFile = null;
                }
                catch (Exception ex)
                {
                    HandleException(logData.System, new Exception(string.Format("Unable to download '{0}' licence file.", licenceFile), ex));
                    licenceFile = null;
                }

                try
                {
                    // Baixa os arquivos de setup
                    serverSetupPath = Path.Combine(Path.GetTempPath(), @"Server_SetupFiles");
                    logPackagerServiceClient.DownloadSetupFiles(logData.System, sourceAddress, serverSetupPath);
                }
                catch (Exception ex)
                {
                    HandleException(logData.System, new Exception("Unable to download server setup files.", ex));
                    serverSetupPath = null;
                }
            }

            logPackagePath = Path.Combine(ApplicationConfiguration.GetApplicationRootDirectory(), string.Format(LOG_PACKAGE_PATH, DateTime.Now));

            if (!Directory.Exists(Path.GetDirectoryName(logPackagePath)))
                Directory.CreateDirectory(Path.GetDirectoryName(logPackagePath));
            else if (File.Exists(logPackagePath))
                File.Delete(logPackagePath);

            using (Ionic.Zip.ZipFile zipFile = new Ionic.Zip.ZipFile(logPackagePath))
            {
                zipFile.AddFile(logDataFile, "\\");

                if (!string.IsNullOrEmpty(screenshotFile))
                    zipFile.AddFile(screenshotFile, "\\");

                if (!string.IsNullOrEmpty(clientPSESetupPath))
                    zipFile.AddDirectory(clientPSESetupPath, string.Format(@"Client_SetupFiles\{0}", Path.GetFileName(clientPSESetupPath)));

                if (!string.IsNullOrEmpty(clientPSEComponentsSetupPath))
                    zipFile.AddDirectory(clientPSEComponentsSetupPath, string.Format(@"Client_SetupFiles\{0}", Path.GetFileName(clientPSEComponentsSetupPath)));

                if (!string.IsNullOrEmpty(clientSystemSetupPath))
                    zipFile.AddDirectory(clientSystemSetupPath, string.Format(@"Client_SetupFiles\{0}", Path.GetFileName(clientSystemSetupPath)));

                if (!string.IsNullOrEmpty(licenceFile))
                    zipFile.AddFile(licenceFile, "\\");

                if (!string.IsNullOrEmpty(serverSetupPath))
                    zipFile.AddDirectory(serverSetupPath, Path.GetFileName(serverSetupPath));

                zipFile.Save();
            }

            return logPackagePath;
        }

        /// <summary>
        /// Empacota informações de log
        /// </summary>
        /// <param name="from">Menor data que os logs serão empacotados</param>
        /// <param name="to">Maior data que os logs serão empacotados</param>
        /// <param name="files">Lista de arquivos a serem empacotados</param>
        /// <returns>Nome do arquivo gerado</returns>
        public string Pack(SystemComponent system, DateTime from, DateTime to)
        {
            if (from.Date > to.Date)
                throw new ArgumentException("The 'from' date cannot be greater than 'to' date.");

            string logPackagePath = null;
            List<string> logDataFiles = new List<string>();

            //string licenceFile = null;

            string pseSetupPath = null;
            string pseComponentsSetupPath = null;
            string systemSetupPath = null;


            // Captura arquivos com erro
            ApplicationConfiguration applicationConfiguration = PSE.Framework.Configuration.ApplicationConfiguration.GetCurrent();
            ErrorLoggingConfigurationSection errorLoggingConfiguration = applicationConfiguration.GetSection<ErrorLoggingConfigurationSection>("errorLoggingConfiguration");

            // Captura caminho do arquivo de log
            string logFilePath = errorLoggingConfiguration.GetLogFilePath(system);

            foreach (string file in Directory.GetFiles(Path.GetDirectoryName(logFilePath), "*Log_*.svclog"))
            {
                DateTime fileDate = File.GetCreationTime(file);

                if (fileDate.Date >= from.Date &&
                    fileDate.Date <= to.Date)
                {
                    logDataFiles.Add(file);
                }
            }

            if (logDataFiles.Count > 0)
            {
                //LogPackagerServiceClient logPackagerServiceClient = new LogPackagerServiceClient();

                //try
                //{
                //    // Baixa o arquivo de licença
                //    licenceFile = Path.Combine(Path.GetTempPath(), string.Format(@"{0}.lic", system.Name));

                //    if (!logPackagerServiceClient.DownloadLicenceFile(licenceFile))
                //        licenceFile = null;
                //}
                //catch (Exception ex)
                //{
                //    HandleException(system, new Exception(string.Format("Unable to download '{0}' licence file.", licenceFile), ex));
                //    licenceFile = null;
                //}

                // Captura arquivos de setup
                pseSetupPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), @"BMFBOVESPA\PSE");
                if (!Directory.Exists(pseSetupPath))
                {
                    HandleException(system, new Exception(string.Format("Setup file '{0}' not found.", pseSetupPath)));
                    pseSetupPath = null;
                }

                pseComponentsSetupPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), @"BMFBOVESPA\PSEComponents");
                if (!Directory.Exists(pseComponentsSetupPath))
                {
                    HandleException(system, new Exception(string.Format("Setup file '{0}' not found.", pseComponentsSetupPath)));
                    pseComponentsSetupPath = null;
                }

                systemSetupPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), string.Format(@"BMFBOVESPA\{0}", system.Name));
                if (!Directory.Exists(systemSetupPath))
                {
                    HandleException(system, new Exception(string.Format("Setup file '{0}' not found.", systemSetupPath)));
                    systemSetupPath = null;
                }


                logPackagePath = Path.Combine(ApplicationConfiguration.GetApplicationRootDirectory(), string.Format(PERIOD_LOG_PACKAGE_PATH, from, to));

                if (!Directory.Exists(Path.GetDirectoryName(logPackagePath)))
                    Directory.CreateDirectory(Path.GetDirectoryName(logPackagePath));
                else if (File.Exists(logPackagePath))
                    File.Delete(logPackagePath);

                using (Ionic.Zip.ZipFile zipFile = new Ionic.Zip.ZipFile(logPackagePath))
                {
                    zipFile.AddFiles(logDataFiles, "\\");

                    //if (!string.IsNullOrEmpty(licenceFile))
                    //    zipFile.AddFile(licenceFile);

                    if (!string.IsNullOrEmpty(pseSetupPath))
                        zipFile.AddDirectory(pseSetupPath, string.Format(@"SetupFiles\{0}", Path.GetFileName(pseSetupPath)));

                    if (!string.IsNullOrEmpty(pseComponentsSetupPath))
                        zipFile.AddDirectory(pseComponentsSetupPath, string.Format(@"SetupFiles\{0}", Path.GetFileName(pseComponentsSetupPath)));

                    if (!string.IsNullOrEmpty(systemSetupPath))
                        zipFile.AddDirectory(systemSetupPath, string.Format(@"SetupFiles\{0}", Path.GetFileName(systemSetupPath)));

                    zipFile.Save();
                }
            }

            return logPackagePath;
        }

        private void SaveScreenshot(UIElement sp, string file)
        {
            double dpiX = 96;

            double dpiY = 96;

            FrameworkElement elem = sp as FrameworkElement;

            if (elem == null)

                return;

            Rect bounds = VisualTreeHelper.GetContentBounds(elem);

            if (bounds.Width == double.PositiveInfinity || bounds.Width == double.NegativeInfinity)

                bounds = new Rect(0, 0, elem.ActualWidth - 5, elem.ActualHeight - 5);

            RenderTargetBitmap bmp = new RenderTargetBitmap((Int32)(bounds.Width * dpiX / 96.0),

            (Int32)(bounds.Height * dpiY / 96.0), dpiX, dpiY, PixelFormats.Pbgra32);

            bmp.Render(elem);

            BitmapEncoder encoder;

            encoder = new JpegBitmapEncoder();

            encoder.Frames.Add(BitmapFrame.Create(bmp));

            using (Stream stm = File.Create(file))
            {
                encoder.Save(stm);
            }
        }

        private void HandleException(SystemComponent system, Exception ex)
        {
            ErrorLogger.WriteLog(system,
                                 PSE.Framework.Configuration.Components.SystemComponents.LogPackager,
                                 LogType.Warning,
                                 ex);
        }
    }
}
